#ifndef SelectionBase_h
#define SelectionBase_h

#include "StepBase.hxx"
#include "HLClonesArray.hxx"
#include "nd280Samples.hxx"
#include <vector>
#include <string>
#include <sstream>

/// The maximum number of steps that is supported.
const UInt_t NMAXSTEPS=50;


/// Base class for an event selection
/// It should implement the methods  
///  - DefineSteps
///  - MakeBox
///  - MakeEventSummary
///  - FillEventSummary

/// This class that handles the registration of steps (cuts and actions),
/// and records which cuts were passed
///
/// The main way you will interact with this class is using the AddStep() function
///
/// If your analysis only has a single "branch" to it, then adding cuts is very simple. All you have to do
/// is define cuts that inherit from the CutBase base class, and add them using
///   AddStep(Step type, "Pretty name",  new YourNewCut());
///
/// You should add them in DefineSteps() method of your selection. The steps should be added in the
/// order in which you call them. You can have up to NMAXSTEPS cuts. For example for the 
/// CC-Inclusive selection
///
///   AddStep(StepBase::kCut,    "event quality",      new EventQualityCut(),           true);
///   AddStep(StepBase::kCut,    "> 0 tracks ",        new TotalMultiplicityCut(),      true);  
///   AddStep(StepBase::kAction, "find leading tracks",new FindLeadingTracksAction());  
///   AddStep(StepBase::kAction, "find vertex",        new FindVertexAction());  
///   AddStep(StepBase::kCut,    "quality+fiducial",   new TrackQualityFiducialCut(),   true);  
///   AddStep(StepBase::kAction, "find veto track",    new FindVetoTrackAction());
///   AddStep(StepBase::kCut,    "veto",               new ExternalVetoCut());
///   AddStep(StepBase::kAction, "find oofv track",    new FindOOFVTrackAction());
///   AddStep(StepBase::kCut,    "External FGD1",      new ExternalFGD1lastlayersCut());
///   AddStep(StepBase::kCut,    "muon PID",           new MuonPIDCut());
///
/// last "true" means the step sequence is broken if cut is not passed (default is "false")

/// Cut branches
///
/// Your analysis can contain several branches that contain common steps. For example, CC0pi, CC1pi and
/// CCNpi branches. You can have up to NMAXBRANCHES branches. There are a few subtleties to be aware of
/// when using branches, which are best explained using the following example code, which would all appear
/// in your yourSelection class. For the CC multi-pion selection

///   Additional actions for the multi-pi selection.
///
///     AddStep(StepBase::kAction, "find_pions", new FindPionsAction());
///     AddStep(StepBase::kAction, "find_tpc1", new FindTPC1TracksAction());

///  Add a split to the trunk with 3 branches.
///     AddSplit(3);

///  First branch is for CC-0pi
///     AddStep(0, StepBase::kCut, "CC-0pi", new NoPionCut());

///  Second branch is for CC-1pi
///     AddStep(1, StepBase::kCut, "CC-1pi", new OnePionCut());

///  Third branch is for CC-Other
///     AddStep(2, StepBase::kCut, "CC-Other", new OthersCut());

///   Set the branch aliases to the three branches
///      SetBranchAlias(0,"CC-0pi",0);
///      SetBranchAlias(1,"CC-1pi",1);
///      SetBranchAlias(2,"CC-Other",2);


class SelectionBase: public TObject{
public:
  SelectionBase(bool forceBreak=true);
  virtual ~SelectionBase(){}
  
  //---- These are mandatory functions --------------

  virtual void DefineSteps(){}// = 0;    /// Define all steps in the selection
  virtual AnaBoxB* MakeBox(){return NULL;}// = 0;
  virtual bool FillEventSummary(const AnaBoxB&, AnaEventB&, Int_t*){return 0;}// = 0;
  virtual nd280Samples::SampleEnum GetSampleEnum(){return nd280Samples::kUnassigned;}// = 0;

  //---------------------------------------------------

  /// Fill the Event Summary using the _allCutsPassed data member (non thread safe)
  void FillEventSummary(const AnaBoxB&, AnaEventB&);

  /// Apply all steps in the selection (the box is created and destroyed intenally)
  bool Apply(AnaEventB& event);

  /// Apply all steps in a threadsafe manner
  bool Apply(AnaEventB& event, AnaBoxB& box, Int_t all_cuts_passed[]);

  /// Apply all steps in the selection provided the box
  bool Apply(AnaEventB& event, AnaBoxB& box);

  /// Return the title of this selection. A nice version of the name.
  const std::string& Title() const{return _title;}

  /// Return the name of this selection. This overrides the TObject::GetName() interface.
  virtual const char* GetName() const {return _name.c_str();}

  /// Return the name of this selection.
  const std::string& Name() const{return _name;}

  /// Set the title of this selection, which is the "nice" version of the selection name,
  void SetTitle(const std::string& title){_title=title;}

  /// Set the name of this selection, which is used internally by the SelectionManager
  void SetName(const std::string& name){_name=name;}

  /// enable and disable selection
  void Enable(){_enabled=true;}
  void Disable(){_enabled=false;}

  // Is this selection enabled ?
  bool IsEnabled() const {return _enabled;}

  /// When using the framework by a fitter (i.e. BANFF) we want to break the step sequence when a cut is not passed, that is 
  /// we are interested about events that pass all cuts. In this case _forceBreak should be true. On the other hand it should be false 
  /// in highland since we might be interested about events that didn't pass a specific cut. 
  bool GetForceBreak() const {return _forceBreak;}

  // Print statistics for this selection
  void PrintStatistics() const;
  void PrintStatistics(const std::string& name) const;
  void PrintStatistics(UInt_t ID) const;

  //-----------------------------------------------------------------

  /// Add a user step to the selection. 
  /// One should specify the branching sequence, the step type, the title and the cut_break (whether to break the step sequence quen a cut is not passed)
  void AddStep(                                                                                StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break=false);
  void AddStep(Int_t b0,                                                                       StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break=false);
  void AddStep(Int_t b0, Int_t b1,                                                             StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break=false);
  void AddStep(Int_t b0, Int_t b1, Int_t b2,                                                   StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break=false);
  void AddStep(Int_t b0, Int_t b1, Int_t b2, Int_t b3,                                         StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break=false);
  void AddStep(Int_t b0, Int_t b1, Int_t b2, Int_t b3, Int_t b4,                               StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break=false);
  void AddStep(Int_t b0, Int_t b1, Int_t b2, Int_t b3, Int_t b4, Int_t b5,                     StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break=false);
  void AddStep(Int_t b0, Int_t b1, Int_t b2, Int_t b3, Int_t b4, Int_t b5, Int_t b6,           StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break=false);
  void AddStep(Int_t b0, Int_t b1, Int_t b2, Int_t b3, Int_t b4, Int_t b5, Int_t b6, Int_t b7, StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break=false);
  void AddStep(const std::vector<UInt_t>& branch_seq,                                          StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break=false);

  // This is the basic function to add the step
  void AddStep(const std::vector<UInt_t>& branch_seq, StepBase* step);

  // This is the basic function to add several step
  void AddSteps(const std::vector<UInt_t>& branch_seq, const std::vector<StepBase*>& steps);

  /// Add a split in the step sequence. The user should specify the number of branches in this split and the branch sequence
  void AddSplit(UInt_t nbranches, Int_t b0=-1, Int_t b1=-1, Int_t b2=-1, Int_t b3=-1, Int_t b4=-1, Int_t b5=-1, Int_t b6=-1, Int_t b7=-1);
  void AddSplit(UInt_t nbranches, const std::vector<UInt_t>& branch_seq);

  void AddBranch(Int_t ibranch, const std::vector<UInt_t>& branch_seq, const std::string& alias="");

  

  /// Replace a step (same structure of AddStep, but for only one step at once)
//  void ReplaceStep(const std::vector<UInt_t>& branch_seq, UInt_t step_nbr, StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break=false);
  
  /// Remove a step
//  void RemoveStep(const std::vector<UInt_t>& branch_seq, UInt_t step_nbr);

  /// Insert a step after the specified cut
//  void InsertStep(const std::vector<UInt_t>& branch_seq, UInt_t step_nbr,StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break=false);

  /// Disable step
//  void DisableStep(const std::vector<UInt_t>& branch_seq, UInt_t step_nbr);





  /// Copy a clone of the steps in range first-last from branch sbranch1 in selection ssel1 to sbranch2 in selection ssel2
  void CopySteps(SelectionBase& sel1, UInt_t branchID1, UInt_t first, UInt_t last, UInt_t branchID2=0);
  void CopySteps(SelectionBase& sel1, const std::string& sbranch1, UInt_t first, UInt_t last,const std::string& sbranch2="trunk");
  void CopySteps(SelectionBase& sel1, UInt_t first, UInt_t last,const std::string& sbranch2="trunk"){
    CopySteps(sel1,"trunk",first,last,sbranch2);
  }
  
  /// Copy a clone of all the steps from branch sbranch1 in selection ssel1 to sbranch2 in selection ssel2
  void CopySteps(SelectionBase& sel1, const std::string& sbranch1="trunk", const std::string& sbranch2="trunk");
    
  
  /// Copy a clone of the steps with number istep from branch sbranch1 in selection ssel1 to sbranch2 in selection ssel2
  void CopyStep(SelectionBase& sel1, const std::string& sbranch1, UInt_t istep, const std::string& sbranch2="trunk");  
  void CopyStep(SelectionBase& sel1, UInt_t istep, const std::string& sbranch2="trunk"){
    CopyStep(sel1,"trunk",istep,sbranch2);
  }


  //-----------------------------------------------------

  /// Get all steps in a given branch provided the branch sequence (individual integers)
  std::vector<StepBase*> GetStepsInBranch(Int_t b0=-1, Int_t b1=-1, Int_t b2=-1, Int_t b3=-1, Int_t b4=-1, Int_t b5=-1, Int_t b6=-1, Int_t b7=-1);

  /// Get all steps in a given branch provided the branch sequence (vector of integers)
  std::vector<StepBase*> GetStepsInBranch(const std::vector<UInt_t>& branch_seq, UInt_t first=0, UInt_t last=NMAXSTEPS-1);

  /// Get all steps in a given branch provided the branch sequence (vector of integers)
  std::vector<StepBase*> GetStepsInBranchWithDummy(const std::vector<UInt_t>& branch_seq, UInt_t first=0, UInt_t last=NMAXSTEPS-1);

  /// Get all steps in a given branch provided the branch alias
  std::vector<StepBase*> GetStepsInBranch(const std::string& branch, UInt_t first=0, UInt_t last=NMAXSTEPS-1);

  /// Get all steps in a given branch provided the branch uniqueID
  std::vector<StepBase*> GetStepsInBranch(UInt_t ID, UInt_t first=0, UInt_t last=NMAXSTEPS-1);

  /// Get all cuts in a given branch provided the branch alias
  std::vector<StepBase*> GetCutsInBranch(const std::string& branch);

  /// Get all cuts in a given branch provided the branch uniqueID
  std::vector<StepBase*> GetCutsInBranch(UInt_t ID);

  /// Get the last step in a given branch provided the branch sequence (individual integers)
  StepBase* GetLastStepInBranch(Int_t b0=-1, Int_t b1=-1, Int_t b2=-1, Int_t b3=-1, Int_t b4=-1, Int_t b5=-1, Int_t b6=-1, Int_t b7=-1);

  /// Get the last step in a given branch provided the branch sequence (vector of integers)
  StepBase* GetLastStepInBranch(const std::vector<UInt_t>& branch);

  /// Get the last step in a given branch provided the branch alias
  StepBase* GetLastStepInBranch(const std::string& branch);

  //-----------------------------------------------------

  /// Convert the branch sequence into a string
  std::string ConvertBranchToString(const std::vector<UInt_t>& branch);

  /// Convert the branch sequence of individual integers into a vector of integers
  std::vector<UInt_t> ConvertBranch(Int_t b0=-1, Int_t b1=-1, Int_t b2=-1, Int_t b3=-1, Int_t b4=-1, Int_t b5=-1, Int_t b6=-1, Int_t b7=-1);
  
  /// Set the branch alias and unique ID provided the branch sequence
  void SetBranchAlias(Int_t ID, const std::string& name, Int_t b0=-1, Int_t b1=-1, Int_t b2=-1, Int_t b3=-1, Int_t b4=-1, Int_t b5=-1, Int_t b6=-1, Int_t b7=-1);
  
  /// Get the branch sequence for a specific branch alias
  std::vector<UInt_t> GetBranchSequence(const std::string& name);

  /// Get the branch sequence for a specific branch unique ID
  std::vector<UInt_t> GetBranchSequence(Int_t ID);

  /// Gets the branch unique ID with a given alias
  Int_t GetBranchUniqueID(const std::string& name) const;

  /// Gets the branch alias for a given branch unique ID
  std::string GetBranchAlias(Int_t ID) const;

  //-----------------------------------------------------

  /// Apply a range of steps in a branch
  bool ApplySteps(const AnaEventB& event, AnaBoxB& box, const std::string& branch, Int_t ifirst=0, Int_t ilast=NMAXSTEPS);

  /// Apply all registered steps
  bool ApplySteps(const AnaEventB& event, AnaBoxB& box);

  /// Apply all registered steps in a threadsafe way
  bool ApplySteps(const AnaEventB& event, AnaBoxB& box, Int_t all_cuts_passed[]);

  /// Return whether the specified cut was passed or not.
  bool GetCutPassed(Int_t icut, Int_t branch=0){return _cut_passed[branch][icut];}

  /// Get the accumulated cut level - the maximum cut number for which all
  /// lower cut numbers were passed.
  Int_t GetAccumCutLevel(Int_t branch=0){return _accum_cut_level[branch];}

  /// Reset all cuts to "not passed", and set the accumulated cut level to 0.
  void InitializeCutLevels();

  /// Reset the maximum cut level for this event
  void ResetMaxAccumCutLevel(){_max_accum_cut_level = 0;}

  /// Get the maximum cut level for this event
  Int_t GetMaxAccumCutLevel(){return _max_accum_cut_level;}

  //-----------------------------------------------------  

  /// Return the number of branches
  UInt_t GetNBranches(){return _branchSequence.size();}

  /// Return all the names of the steps, in the order they were added.
  std::vector<std::string> GetStepNames(Int_t ibranch=0);

  /// Return all the names of the cuts, in the order they were added.
  std::vector<std::string> GetCutNames(Int_t ibranch=0);

  Int_t GetCutNumber(const std::string& title, Int_t ID=0);

  /// Return the number of steps in a given branch
//  UInt_t GetNSteps(Int_t branch);

  /// Return the number of cuts that have been added.
  UInt_t GetNMaxCuts();

  /// Return the number of cuts in a given branch
  UInt_t GetNCuts(Int_t branch);

  /// By default EventSummary class is created and filled when the selection is passed. But this might be necessary also for events not passing the selection
  /// The user can force the EventSummary to be filled always. 
  void SetForceFillEventSummary(bool force){_forceFillEventSummary= force;}

  /// Returns the flag to fill the EventSummary even when the selection is not passed
  bool GetForceFillEventSummary() const { return _forceFillEventSummary;}

  /// Print out the index, name and title of each step for a given branch (no argument for all branches)
  void DumpSteps(const std::string& branch="", bool onlycuts=false);
  void DumpSteps(Int_t ID, bool onlycuts=false);

  /// Print out the index, name and title of each cut for a given branch (no argument for all branches)
  void DumpCuts(Int_t branch=-1){DumpSteps(branch,true);}

  /// Set the Run periods (defined in AnalysisUtils) for which this selection is valid
  /// By default all run periods are valid
  void SetValidRunPeriods(std::string runPeriods);

  /// Method to see whether this selection should be applied to the given run period
  bool IsValidRun(int runPeriod){return _validRunPeriods[runPeriod];}

  //-----------------------------------------------------  

  ClassDef(SelectionBase, 1);
    
protected:

  /// Add a branch unique ID to all steps in that branch
  void AddBranchUniqueID(std::vector<StepBase*>& steps, UInt_t ibranch);

  /// Apply all steps recursively provided the first step
  bool ApplyStepRecursive(const AnaEventB& event, AnaBoxB& box,const StepBase& step);

  /// Apply the step. This is the
  /// important function that calls Apply() in the relevant step.
  bool ApplyStep(const AnaEventB& event, AnaBoxB& box, const StepBase& step, Int_t branch=-1);

  /// Apply all steps recursively provided the first step in a threadsafe way
  bool ApplyStepRecursive(const AnaEventB& event, AnaBoxB& box,const StepBase& step, Int_t all_cuts_passed[]);

  /// Set that the specified cut was passed. Increments the accumulated cut
  /// level.
  void CutPassed(Int_t icut, Int_t branch);

  /// Set whether the current cut was passed, and increment the current cut
  /// counter. Return value is the same as the input "passed" value. Calls
  /// the other CutPassed interface if "passed" is true.
  bool CutPassed(bool passed, Int_t branch);

  bool CutPassed(bool ok, const StepBase& step);

  /// Check whetehr a cut is passed and set the relevant all_cuts_passed field if it is not passed
  /// THis is the threadsafe version of the selection
  bool CutPassed(bool ok, const StepBase& step, Int_t all_cuts_passed[]);

protected:

  /// The name of the selection
  std::string _name;

  /// the nice name of the selection
  std::string _title;

  /// is this selection enabled ?
  bool _enabled;

  /// The cuts that were passed.
  bool _cut_passed[NMAXBRANCHES][NMAXSTEPS]; //!

  /// The accumulated cut level - the maximum cut number for which all previous
  /// cuts were also passed.
  Int_t _accum_cut_level[NMAXBRANCHES];  //!

  /// The current cut level.
  Int_t _current_cut[NMAXBRANCHES];  //!

  /// the maximum accumulated cut level for the current event
  Int_t _max_accum_cut_level;  //!

  /// The top level steps in the trunk
  std::vector<StepBase*> _firstSteps;

  /// Association between a branch unique ID (entry in vector) and branch alias
  std::vector<std::string> _branchAlias;  

  /// Association between a branch unique ID (entry in vector) and branch sequence
  std::vector< std::vector<UInt_t> > _branchSequence; 

  /// Number of cuts in each branch
  std::vector<UInt_t> _nCutsInBranch; 

  /// Used for statistics
  std::vector<UInt_t > _nEventsPassed;    //!
  Int_t _allCutsPassed[NMAXBRANCHES];  //!
  int _cut_passed_stats[NMAXBRANCHES][NMAXSTEPS];

  /// When using the framework by a fitter (i.e. BANFF) we want to break the step sequence when a cut is not passed, that is 
  /// we are interested about events that pass all cuts. In this case _forceBreak should be true. On the other hand it should be false 
  /// in highland since we might be interested about events that didn't pass a specific cut. 
  bool _forceBreak;

  /// By default EventSummary class is created and filled when the selection is passed. But this might be necessary also for events not passing the selection
  /// The user can force the EventSummary to be filled always. 
  bool _forceFillEventSummary;    //!

  /// Boolean array to store valid run periods for this selection
  /// Should use enumeration in future to set size of this
  bool _validRunPeriods[10]; //!

};


#endif
